#include "task.hpp"
#include "job.hpp"
#include "kernel.hpp"
#include "helper.hpp"
#include "util.hpp"
#include "clock.hpp"
#include "FSCP_Utils.hpp"
#include "logger.hpp"
#include <iomanip>

using namespace qOS;

const iteration_t task::PERIODIC = INT32_MIN;
const iteration_t task::INDEFINITE = task::PERIODIC;
const iteration_t task::SINGLE_SHOT = 1;

const uint32_t task::BIT_INIT = 0x00000001UL;
const uint32_t task::BIT_ENABLED = 0x00000002UL;
const uint32_t task::BIT_QUEUE_RECEIVER = 0x00000004UL;
const uint32_t task::BIT_QUEUE_FULL = 0x00000008UL;
const uint32_t task::BIT_QUEUE_COUNT = 0x00000010UL;
const uint32_t task::BIT_QUEUE_EMPTY = 0x00000020UL;
const uint32_t task::BIT_SHUTDOWN = 0x00000040UL;
const uint32_t task::BIT_REMOVE_REQUEST = 0x00000080UL;
const uint32_t task::EVENT_FLAGS_MASK = 0xFFFFF000UL;
const uint32_t task::QUEUE_FLAGS_MASK = 0x0000003CUL;

//==============================================================================
constexpr iteration_t TASK_ITER_VALUE(iteration_t x)
{
    return ((x < 0) && (x != task::PERIODIC)) ? -x : x;
}
//==============================================================================
void task::activities(event_t e)
{
    // Check if parent job is paused
    if (parentJob != nullptr && parentJob->isPaused()) {
        return;  // Skip execution if parent job is paused
    }
    
    // Check job timing constraints
    if (parentJob != nullptr) {
        if (!parentJob->checkTimingConstraints()) {
            return;  // Timing constraints not satisfied
        }
    }

    // Check task's own timing constraints
    if (!isReady(clock::getTick())) {
        return;  // Task not ready to execute yet
    }

    if (isDeadlineMissed(clock::getTick())) {
        // Task deadline missed, notify parent job and skip execution
        if (parentJob != nullptr) {
            notifyParentJob(false, true, false, static_cast<uint32_t>(-1));
        }
        return;
    }

    // Update task slack time
    updateSlack(clock::getTick());

    // Check job execution mode and dependencies
    if (parentJob != nullptr) {
        // For sequential execution, check if this task should run now
        if (parentJob->getExecutionMode() == JobExecutionMode::SEQUENTIAL) {
            if (!parentJob->shouldExecuteTask(this, m_securityToken)) {
                return;  // Not this task's turn yet
            }
        }
        // For parallel execution, all tasks can run simultaneously
        // For pipeline and conditional modes, additional logic would be needed
    }
    
    // Notify parent job that task is starting
    notifyParentJob(true, false, true, 0);
    
    if (nullptr != callback) {
        try {
            callback(e, m_securityToken);
            // Notify parent job that task completed successfully
            notifyParentJob(false, true, true, 0);
        } catch (...) {
            // Notify parent job that task encountered an error
            notifyParentJob(false, true, false, 1);
        }
    } else {
        // No callback defined, mark as completed
        notifyParentJob(false, true, true, 0);
    }
}
//==============================================================================
void task::setFlags(const uint32_t xFlags, const bool value) noexcept
{
    if (value) {
        bits::multipleSet(this->flags, xFlags);
    }
    else {
        bits::multipleClear(this->flags, xFlags);
    }
}
//==============================================================================
bool task::getFlag(const uint32_t flag) const noexcept
{
    const uint32_t xBit = this->flags & flag;
    return (0UL != xBit);
}

//==============================================================================
bool task::setTimingAttributes(const TaskTimingAttr& attr) noexcept
{
    timing_attr = attr;
    return true;
}

//==============================================================================
TaskTimingAttr task::getTimingAttributes(void) const noexcept
{
    return timing_attr;
}

//==============================================================================
uint64_t task::updateSlack(uint64_t current_time) noexcept
{
    if (timing_attr.deadline > current_time) {
        timing_attr.slack = timing_attr.deadline - current_time;
    }
    else {
        timing_attr.slack = 0;
    }

    return timing_attr.slack;
}

//==============================================================================
bool task::isDeadlineMissed(uint64_t current_time) const noexcept
{
    return (timing_attr.deadline > 0) && (current_time > timing_attr.deadline);
}

//==============================================================================
bool task::isReady(uint64_t current_time) const noexcept
{
    return (timing_attr.release_time <= current_time);
}
//==============================================================================
priority_t task::getPriority(void) const noexcept
{
    return priority;
}
//==============================================================================
bool task::setPriority(const priority_t pValue) noexcept
{
    bool retValue = false;

    if (pValue < static_cast<priority_t>(Q_PRIORITY_LEVELS)) {
        priority = pValue;
        retValue = true;
    }

    return retValue;
}
//==============================================================================
cycles_t task::getCycles(void) const noexcept
{
    return cycles;
}
//==============================================================================
taskState task::getState(void) const noexcept
{
    taskState retValue;

    retValue = static_cast<taskState>(getFlag(BIT_SHUTDOWN));
    if (taskState::DISABLED_STATE != retValue) { /*Task is awaken*/
        retValue = static_cast<taskState>(getFlag(BIT_ENABLED));
    }

    return retValue;
}
//==============================================================================
bool task::deadLineReached(void) const noexcept
{
    bool retValue = false;

    if (getFlag(BIT_ENABLED)) {
        const iteration_t iters = iterations;
        /*task should be periodic or must have available iters*/
        if ((TASK_ITER_VALUE(iters) > 0) || (PERIODIC == iters)) {
            /*check the time deadline*/
            const clock_t interval = time.getInterval();
            const bool expired = time.expired();

            if ((0UL == interval) || expired) {
                retValue = true;
            }
        }
    }

    return retValue;
}
//==============================================================================
bool task::setState(const taskState s) noexcept
{
    bool retValue = false;

    switch (s) {
        case taskState::DISABLED_STATE: case taskState::ENABLED_STATE: // skipcq: CXX-C1001
            if (s != static_cast<taskState>(getFlag(BIT_ENABLED))) {
                setFlags(BIT_ENABLED, static_cast<bool>(s));
                time.reload();
            }
            retValue = true;
            break;
        case taskState::ASLEEP_STATE:
            setFlags(BIT_SHUTDOWN, false);
            retValue = true;
            break;
        case taskState::AWAKE_STATE:
            setFlags(BIT_SHUTDOWN, true);
            retValue = true;
            break;
        default:
            break;
    }

    return retValue;
}
//==============================================================================
void task::setIterations(const iteration_t iValue) noexcept
{
    if (iValue > 0) {
        iterations = -iValue;
    }
    else if (PERIODIC == iValue) {
        iterations = PERIODIC;
    }
    else {
        /*nothing to do, return qFalse*/
    }
}
//==============================================================================
bool task::setTime(const qOS::duration_t tValue) noexcept
{
    return time.set(tValue);
}
//==============================================================================
bool task::setCallback(const taskFcn_t tCallback) noexcept
{
    bool retValue = false;

    if (tCallback != callback) {
        callback = tCallback;
        #if ((Q_FSM == 1) || (Q_CLI == 1))
            aObj = nullptr;
        #endif
        retValue = true;
    }

    return retValue;
}
//==============================================================================
bool task::setData(void *arg) noexcept
{
    bool retValue = false;

    if (arg != taskData) {
        taskData = arg;
        retValue = true;
    }

    return retValue;
}
//==============================================================================
bool task::setName(const char *tName) noexcept
{
    bool retValue = false;
    const size_t nl = util::strlen(tName , sizeof(name));
    /*cstat -MISRAC++2008-5-14-1*/
    if ((nullptr != getContainer()) && (nl > 0U) && (nl < sizeof(name))) {
        if (nullptr == os.getTaskByName(tName)) {
            (void)util::strcpy(name, tName , sizeof(name));  // skipcq: CXX-C1000
            retValue = true;
        }
    }
    /*cstat +MISRAC++2008-5-14-1*/
    return retValue;
}
//==============================================================================
const char* task::getName(void) const noexcept
{
    return name;  // skipcq: CXX-C1000
}
//==============================================================================
trigger task::queueCheckEvents(void) noexcept
{
    trigger retValue = trigger::None;

    if (nullptr != aQueue) {
        const bool fullFlag = getFlag(BIT_QUEUE_FULL);
        const bool countFlag = getFlag(BIT_QUEUE_COUNT);
        const bool receiverFlag = getFlag(BIT_QUEUE_RECEIVER);
        const bool emptyFlag = getFlag(BIT_QUEUE_EMPTY);
        const size_t qCount = aQueue->count();  /*current queue count*/

        /*check the queue events in the corresponding precedence order*/
        /*cstat -MISRAC++2008-5-14-1*/
        if (fullFlag && aQueue->isFull()) { /*isFull() is known to not have side effects*/
            retValue = trigger::byQueueFull;
        }
        else if ((countFlag) && (qCount >= aQueueCount)) {
            retValue = trigger::byQueueCount;
        }
        else if (receiverFlag && (qCount > 0U)) {
            retValue = trigger::byQueueReceiver;
        }
        else if (emptyFlag && aQueue->isEmpty()) {  /*isEmpty() is known to not have side effects*/
            /*qQueue_IsEmpty is known to not have side effects*/
            retValue = trigger::byQueueEmpty;
        }
        else {
            /*this case does not need to be handled*/
        }
        /*cstat +MISRAC++2008-5-14-1*/
    }

    return retValue;
}
//==============================================================================
size_t task::getID(void) const noexcept
{
    return entry;
}
//==============================================================================
bool task::attachQueue(queue &q, const queueLinkMode mode, const size_t arg) noexcept
{
    bool retValue = false;

    if (q.isInitialized()) {
        setFlags(static_cast<uint32_t>(mode) & QUEUE_FLAGS_MASK, 0U != arg);
        if (queueLinkMode::QUEUE_COUNT == mode) {
            aQueueCount = arg;
        }
        aQueue = (arg > 0U) ? &q : nullptr;
        retValue = true;
    }

    return retValue;
}
//==============================================================================
void * const & task::getBindedObject(void) const noexcept
{
    return aObj;
}
//==============================================================================
queue* task::getQueue(void) noexcept
{
    return aQueue;
}
//==============================================================================
bool task::setParentJob(Job *job, uint64_t securityToken) noexcept
{
    // Enhanced Token Execution Check for functional safety
    if (job != nullptr && securityToken != job->GetSecurityToken()) {
        logger::out(logger::error) << "SECURITY BREACH: Invalid token in setParentJob"
                                 << " (expected: " << job->GetSecurityToken()
                                 << ", received: " << securityToken << ")";
        
        if (nullptr != os.mRunningErrorHandler) {
            char buf[256];
            snprintf(buf, sizeof(buf), "Functional safety: Security token mismatch in setParentJob");
            os.mRunningErrorHandler(FSCP_ErrorType::SECURITY_BREACH, buf, nullptr);
        }
        return false;
    }

    if (parentJob != nullptr) {
        return false;  // Task already has a parent Job
    }
    parentJob = job;
    return true;
}
//==============================================================================
Job* task::getParentJob(void) const noexcept
{
    return parentJob;
}
//==============================================================================
bool task::hasParentJob(void) const noexcept
{
    return parentJob != nullptr;
}
//==============================================================================
taskFcn_t task::getCallback(void) const noexcept
{
    return callback;
}
//==============================================================================
void task::notifyParentJob(bool started, bool completed, bool success, uint32_t errorCode)
{
    // Enhanced security check before parent job notification
    if (m_securityToken == 0) {
        logger::out(logger::error) << "SECURITY: Cannot notify parent with invalid token";
        if (nullptr != os.mRunningErrorHandler) {
            os.mRunningErrorHandler(FSCP_ErrorType::TOKEN_CORRUPTION, 
                                   "Token corruption detected in notifyParentJob", nullptr);
        }
        return;
    }
    
    if (parentJob != nullptr) {
        // Find the task index in the parent job
        size_t taskIndex = parentJob->findTaskIndex(this, m_securityToken);
        if (taskIndex != SIZE_MAX) {
            if (started) {
                parentJob->notifyTaskStarted(taskIndex, m_securityToken);
            }
            if (completed) {
                if (success) {
                    parentJob->notifyTaskCompleted(taskIndex, true, m_securityToken);
                }
                else {
                    parentJob->notifyTaskError(taskIndex, errorCode, m_securityToken);
                }
            }
        }
    }
}
